import itertools
import pyzipper
import concurrent.futures

# 定义文件路径和密码长度
filename = "D:\\BaiduNetdiskDownload\\2023\\2.zip"
chars = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$%&'()*+,-./:;<=>?@[]^_{|}~`"
min_length = 5  # 最小密码长度
max_length = 6  # 最大密码长度


# 解压文件的函数
def extract_zip(zip_file, password):
    try:
        with pyzipper.AESZipFile(zip_file) as zf:
            zf.pwd = password.encode('utf-8')  # 设置密码
            zf.extractall()  # 尝试解压
            print(f"Password found: {password}")
            return password
    except RuntimeError:
        return None  # 密码错误
    except Exception as e:
        print("Error:", e)
        return None


# 用于分配任务给不同线程的函数
def password_cracker_chunk(chunk, zip_file):
    for password_tuple in chunk:
        password = "".join(password_tuple)
        result = extract_zip(zip_file, password)
        if result:  # 如果密码正确
            return result
    return None


# 生成密码的所有可能组合
def generate_passwords(min_length, max_length, chars):
    for length in range(min_length, max_length + 1):
        yield from itertools.product(chars, repeat=length)


# 将密码拆分成多个部分以进行并行处理
def divide_passwords(passwords, num_chunks):
    chunk_size = len(passwords) // num_chunks
    return [passwords[i:i + chunk_size] for i in range(0, len(passwords), chunk_size)]


# 主函数，执行多线程密码破解
def multi_threaded_crack():
    passwords = list(generate_passwords(min_length, max_length, chars))  # 生成所有密码组合
    chunks = divide_passwords(passwords, 4)  # 将密码分成4个部分，分配给4个线程

    with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
        futures = [executor.submit(password_cracker_chunk, chunk, filename) for chunk in chunks]

        for future in concurrent.futures.as_completed(futures):
            result = future.result()
            if result:
                print(f"Password found: {result}")
                return result  # 找到密码后返回并停止
    return None


# 调用多线程破解函数
if __name__ == "__main__":
    found_password = multi_threaded_crack()
    if not found_password:
        print("Failed to find the password.")
